package com.chenziwen.scala.chapter05

/**
  * 和Java一样使用extends关键字，在定义中给出子类需要而超类没有的方法和字段，或者重写超类的方法。
  * 如果类声明为final，它将不能被继承。如果单个方法声明为final，将不能被重写
  * Scala明确规定，只要出现override情况，一定要显式声明关键字override
  */
object Scala08_Extends {
  def main(args: Array[String]): Unit = {
    // 子类可以继承父类的属性
    val monkey = new Monkey()
    monkey.name = "sun wu kong"
    println(monkey.name)
    // 子类可以继承父类的方法
    println(monkey.test)

    // 子类不能直接访问父类的私有属性或方法（因为private是访问控制权限，子类虽然继承了父类的属性和方法，但是私有化的情况下，子类不能直接访问）
    //println(monkey.age)

    println(monkey.test1)


    // 类型检查和转换（类比于Java中的对象比较）
    println("string".isInstanceOf[String])
    println(classOf[String])

    def printWeight(animal: Animal): Unit = {
      if (animal.isInstanceOf[Monkey]) {
        val monkey = animal.asInstanceOf[Monkey]
        println("猴子体重：" + monkey.weight)
      } else if (animal.isInstanceOf[Tiger]) {
        val tiger = animal.asInstanceOf[Tiger]
        println("老虎体重:" + tiger.weight)
      }
    }

    val animal: Animal = new Monkey()
    printWeight(animal)
    val animal2: Animal = new Tiger()
    printWeight(animal2)


    val car = new LittleCar("littleCar")
    println(car.cname)
    // 此处编译成Java字节码文件运行时，会有一个Java动态绑定技术存在
    // Java动态绑定技术：Java在调用对象的成员方法时会将方法和对象的实际内存进行绑定，然后调用内存中的方法。
    println(car.tag)
    println(car.age)
    println(car.size)

    // 匿名子（实现）类
    val anonymousCar = new Car("anonymousCar") {
      override def abstractMethod: Unit = {
        println("匿名实现类")
      }

      override var size: Int = 50
      override var age: Int = 30
    }
    anonymousCar.abstractMethod
  }


}

class Animal {
  var name: String = _

  private var age: Int = _

  def test = "123"

  def test1 = "父类中的非抽象方法"
}

class Monkey extends Animal {
  // 方法重写（重写一个“非抽象方法”需要用override修饰符），调用超类的方法使用super关键字
  override def test1 = {
    //println(super.test1)
    "子类重写了父类中的非抽象方法"
  }

  var weight: Int = 80
}

class Tiger extends Animal {
  var weight: Int = 180
}

// 抽象类
// 可以通过abstract关键字标记不能被实例化的类。方法不用标记abstract，只要省掉方法体即可。
// 抽象类可以拥有抽象字段，抽象字段就是没有初始值的字段。
abstract class Car(name: String) {
  var cname: String = name

  // 声明了属性的同时，还会有对应的get方法在底层生成（tag()）
  val tag: String = "car tag"
  // 抽象属性，如果一个类中存在了抽象的属性或方法，那么这个类必须是抽象的
  var age: Int
  var size: Int

  // 抽象方法
  def abstractMethod
}

// 只有主构造器可以调用父类的构造器。子类的辅助构造器永远不可能直接调用父类的构造器。在Scala的构造器中，你不能调用super(params)
// 如下写法，将子类的主构造器中的参数，传递给父类的构造器
class LittleCar(littleCarName: String) extends Car(littleCarName) {

  // 声明了属性，也会有get方法生成，在Scala中需要明确显式的声明（使用override关键字）属性的重写
  override val tag: String = "das auto"

  // var只能重写另一个抽象的var（声明未初始化的变量就是抽象的变量）（因为普通的var，会在底层生成两个方法）
  override var age: Int = 20
  // 抽象属性重写时，可以不用增加override关键字
  var size: Int = 30

  // 注：子类重写抽象方法不需要override，加上也没问题
  def abstractMethod: Unit = {
    println("实现父类的抽象方法")
  }

}
